// -*-mode:c++; coding:utf-8-*-

#ifndef _XBASE_NAMING_STRATEGY_HPP_
#define _XBASE_NAMING_STRATEGY_HPP_

#include <string>
#include <sstream>

#include <boost/regex.hpp>

#include "constants.hpp"
#include "xbase_utility.hpp"
#include "fs.hpp"
#include "md5.hpp"

#include "tablet_name_parser.hpp"

namespace xbase {

	// 获取目录等名称的类
	//
	// 在这里，统一用 path 表示全路径
	struct naming_strategy {
		static inline
		std::string server_dirname(const std::string &p_hostname,
					   uint16_t p_port,
					   uint64_t p_start_code);

		static inline
		std::string group_dirname(const std::string &p_group_name);
		
		static inline
		std::string group_path (const std::string &p_tablet_path,
					const std::string &p_group_name);

		// 1207124083198852, 16位十进制整数
		static inline
		std::string timestamp_string(uint64_t p_timestamp) {
			std::ostringstream out;
			out.width(16);
			out.fill('0');
			out << p_timestamp;
			return out.str();
		}

		static inline
		std::string sstable_name(uint64_t p_sstable_timestamp) {
			return timestamp_string(p_sstable_timestamp);
		}
		
		static inline
		std::string sstable_path(const std::string &p_group_path,
					 uint64_t p_sstable_timestamp);

		static inline
		std::string sstable_ref_path(const std::string &p_group_path,
					     uint64_t p_sstable_timestamp,
					     const std::string &p_ref_table_name,
					     const uint64_t p_ref_tablet_id,
					     const std::string &p_ref_start_row,
					     const std::string &p_ref_end_row) {
			fs::path _path = p_group_path;
			char row[33];
			if(p_ref_start_row.empty())
			{
				row[0] = '\0';
			}
			else
			{
				md5_string(p_ref_start_row.c_str(),
					   static_cast<int>(p_ref_start_row.size()),
					   row);
			}
			std::ostringstream out;
			out << p_ref_table_name
			    << GENERAL_SEPARATOR
			    << row
			    << GENERAL_SEPARATOR
			    << p_ref_tablet_id;
			_path /= sstable_name(p_sstable_timestamp) +
				GENERAL_SEPARATOR + out.str();
			return _path.string();
		}

		static inline
		std::string compact_sstable_path(const std::string &p_group_path,
						 uint64_t p_sstable_timestamp);

		static inline
		std::string deleted_sstable_path(const std::string &p_group_path,
						 uint64_t p_sstable_timestamp);

		static inline
		uint64_t get_timestamp_from_sstable_name(const std::string &p_sstable_name);

		static inline
		bool is_sstable_file(const std::string &p_filename);
		static inline
		bool is_sstable_ref(const std::string &p_filename);

		static inline
		std::string get_tablet_pathname_from_sstable_ref_name(const std::string &p_sstable_ref_name) {
			// find first ','
			std::string::size_type _pos = p_sstable_ref_name.find(GENERAL_SEPARATOR_CHAR);
			// advance pos to skip ','
			++_pos;
			// return left part
			return p_sstable_ref_name.substr(_pos);
		}

		static inline
		std::string ref_tablet_path(const std::string &p_root_path,
					    const std::string &p_ref_tablet_pathname) {
			return p_root_path + "/" + TABLET_PREFIX + p_ref_tablet_pathname;
		}

		static inline
		std::string tablet_path(const std::string &p_root_path,
					const tablet_name_parser &parser);
		
		static inline
		std::string tablet_path(const std::string &p_root_path,
					const std::string &p_table_name,
					uint64_t p_tablet_id,
					const std::string &p_start_row,
					const std::string &p_end_row);

		static inline
		std::string get_table_name_from_tablet_name(const std::string &p_tablet_name);

		static inline
		std::string tablet_name(uint64_t p_tablet_id,
					const std::string &p_table_name,
					const std::string &p_start_row,
					const std::string &p_end_row);

		static inline
		std::string groupname_for_dirname(const std::string &p_group_dirname);

		static inline
		fs::path oldlog_path(const fs::path &p_tablet_path);

		static inline
		bool is_trash_file(const std::string &p_filename) {
			static const std::string _trash_prefix(TRASH_SSTABLE_PREFIX);
			return (p_filename.size() > _trash_prefix.size())
				&& std::equal(_trash_prefix.begin(), _trash_prefix.end(),
					      p_filename.begin());
				   
		}

		static inline
		std::string trash_filename(const std::string &p_filename) {
			const std::string _trash_prefix(TRASH_SSTABLE_PREFIX);
			return _trash_prefix + p_filename;
		}

		static inline
		std::string deleted_filename(const std::string &p_filename) {
			const std::string _deleted_prefix(DELETED_SSTABLE_PREFIX);
			return _deleted_prefix + p_filename;
		}

		static inline
		bool is_deleted_file(const std::string &p_filename) {
			static const std::string _deleted_prefix(DELETED_SSTABLE_PREFIX);
			return (p_filename.size() > _deleted_prefix.size())
				&& std::equal(_deleted_prefix.begin(), _deleted_prefix.end(),
					      p_filename.begin());
				   
		}

		static inline
		std::string composited_tablet_group_name(const std::string &tablet_name,
							 const std::string &group_name) {
			return tablet_name + " " + group_name; // 这要求表名中不包含空格。重要！
		}

		static inline
		std::string make_scan_root_meta_start_row(const std::string &table_name,
						const std::string &start_row_name) {
			return table_name + ',' + dump_string_hex(start_row_name);
		}

		static inline
		std::string make_scan_root_meta_end_row(const std::string &table_name,
					      const std::string &end_row_name) {
			if(end_row_name.empty())
			{
				return table_name + '-'; // '-' == ',' + 1
			}
			else
			{
				return table_name + ',' + dump_string_hex(end_row_name);
			}
		}
	};

	inline
	std::string naming_strategy::server_dirname(const std::string &p_hostname,
						    uint16_t p_port,
						    uint64_t p_start_code)
	{
		std::stringstream out;
		out << TABLET_SERVER_PREFIX << p_hostname
		    << GENERAL_SEPARATOR << p_port
		    << GENERAL_SEPARATOR << p_start_code;
		return out.str();
	}

	inline
	std::string naming_strategy::group_dirname(const std::string &p_group_name)
	{
		std::string _name = GROUP_DIR_PREFIX;
		_name += p_group_name;
		return _name;
	}

	inline
	std::string naming_strategy::group_path (const std::string &p_tablet_path,
						 const std::string &p_group_name)
	{
		std::string _name = GROUP_DIR_PREFIX;
		_name += p_group_name;

		fs::path _path = p_tablet_path;
		_path = _path / _name;
		
		return _path.string();
	}

	inline
	std::string naming_strategy::sstable_path(const std::string &p_group_path,
						  uint64_t p_sstable_timestamp)
	{
		std::ostringstream out;
		out << p_group_path << "/"
		    << sstable_name(p_sstable_timestamp);
		return out.str();
	}

	inline
	std::string naming_strategy::compact_sstable_path(const std::string &p_group_path,
							  uint64_t p_sstable_timestamp)
	{
		std::ostringstream out;
		out << p_group_path << "/" << COMPACT_TMPFILE_PREFIX
		    << sstable_name(p_sstable_timestamp);
		return out.str();
	}

	inline
	std::string naming_strategy::deleted_sstable_path(const std::string &p_group_path,
							  uint64_t p_sstable_timestamp)
	{
		std::ostringstream out;
		out << p_group_path << "/" << DELETED_SSTABLE_PREFIX
		    << sstable_name(p_sstable_timestamp);
		return out.str();
	}

	inline
	uint64_t naming_strategy::get_timestamp_from_sstable_name(const std::string &p_sstable_name)
	{
		std::stringstream stream;
		stream << p_sstable_name;
		uint64_t _timestamp;
		stream >> _timestamp;
		return _timestamp;
	}

	// 1207124083198852, 16位十进制整数
	inline
	bool naming_strategy::is_sstable_file(const std::string &p_filename)
	{
		static const boost::regex _regex("\\d{16}");
		return boost::regex_match(p_filename, _regex);
	}

	// 1207124083198852.test1,[start_row],1207124083198852.120712xxxxxxxxxx , 16位十进制整数 . 子表名 . 原sstable文件名
	// 1207124083198852,test1,MD5[start_row],1207124083198852 , 16位十进制整数 , 子表名
	inline
	bool naming_strategy::is_sstable_ref(const std::string &p_filename)
	{
		static const boost::regex _regex("\\d{16},[^,]+,[^,]*,\\d{16}");
		return boost::regex_match(p_filename, _regex);
	}

	inline
	std::string get_tablet_pathname_from_sstable_ref_name(const std::string &p_sstable_ref_name)
	{
		// find first ','
		std::string::size_type _pos = p_sstable_ref_name.find(GENERAL_SEPARATOR_CHAR);
		// return left part
		return p_sstable_ref_name.substr(_pos);
	}

	inline
	std::string naming_strategy::tablet_path(const std::string &p_root_path,
						 const tablet_name_parser &parser)
	{
		return naming_strategy::tablet_path(
			p_root_path,
			parser.table_name(),
			parser.tablet_id(),
			parser.start_row(),
			""	// end row not used currently
			);
	}
	
	inline
	std::string naming_strategy::tablet_path(const std::string &p_root_path,
						 const std::string &p_table_name,
						 uint64_t p_tablet_id,
						 const std::string &p_start_row,
						 const std::string &p_end_row)
	{
		if(p_tablet_id == 0)
		{
			return p_root_path + "/" + TABLET_PREFIX + p_table_name;
		}
		else
		{
			char row[33];
			if(p_start_row.empty())
			{
				row[0] = '\0';
			}
			else
			{
				md5_string(p_start_row.c_str(),
					   static_cast<int>(p_start_row.size()),
					   row);
			}
			std::ostringstream out;
			out << p_root_path
			    << "/" << TABLET_PREFIX
			    << p_table_name
			    << GENERAL_SEPARATOR
			    << row
			    << GENERAL_SEPARATOR
			    << p_tablet_id;
			return out.str();
		}
	}

	inline
	std::string naming_strategy::get_table_name_from_tablet_name(const std::string &p_tablet_name)
	{
		std::string::size_type _pos = p_tablet_name.find(GENERAL_SEPARATOR_CHAR);
		return p_tablet_name.substr(0, _pos);
	}

	inline
	std::string naming_strategy::tablet_name(uint64_t p_tablet_id,
						 const std::string &p_table_name,
						 const std::string &p_start_row,
						 const std::string &p_end_row)
	{
		if (p_tablet_id == 0) // 是根元子表，其表名即为子表名
		{
			return p_table_name;
		}
		else		// 否则，根据规则拼起来
		{
			std::ostringstream out;
			out << p_table_name
			    << GENERAL_SEPARATOR
			    << dump_string_hex(p_start_row)
			    << GENERAL_SEPARATOR
			    << p_tablet_id;
			return out.str();
		}
	}

	inline
	std::string naming_strategy::groupname_for_dirname(const std::string &p_group_dirname)
	{
		std::string _prefix = GROUP_DIR_PREFIX;
		if((p_group_dirname.size() > _prefix.size()) &&
		   (p_group_dirname.substr(0, _prefix.size()) == _prefix))
			return p_group_dirname.substr(_prefix.size());
		else
			return std::string();
	}

	inline
	fs::path naming_strategy::oldlog_path(const fs::path &p_tablet_path)
	{
		return p_tablet_path / TABLET_OLDLOG;
	}

} // namespace xbase

#endif	// _XBASE_NAMING_STRATEGY_HPP_
